#ifndef UTIL_H_
#define UTIL_H_

#include "npfunctions.h"

namespace utils {

class IdentifierToString {
public:
  explicit IdentifierToString(NPIdentifier identifier)
    : identifier_name_(NULL) {
      identifier_name_ = NPN_UTF8FromIdentifier(identifier);
  }
  const char* identifier_name() const { return identifier_name_; }
  operator const char*() const { return identifier_name_; }
  ~IdentifierToString() { if (identifier_name_) NPN_MemFree(identifier_name_); }

private:
  // Disable evil constructors.
  IdentifierToString();
  IdentifierToString(const IdentifierToString&);

  char* identifier_name_;
};

#ifdef OS_WIN
class Utf8ToUnicode {
public:
  explicit Utf8ToUnicode(const char* utf8data, unsigned int datalen = -1)
    : buffer_(NULL) {
    int size = MultiByteToWideChar(CP_UTF8, 0, utf8data, datalen, 0, 0);
    if (size > 0)
      buffer_ = new WCHAR[size + 1];
    if (buffer_) {
      MultiByteToWideChar(CP_UTF8, 0, utf8data, datalen, buffer_, size);
      buffer_[size] = 0;
    }
  }
  operator WCHAR*() const { return buffer_; }
  WCHAR** operator &() { return &buffer_; }
  ~Utf8ToUnicode() { if (buffer_) delete[] buffer_; }

private:
  Utf8ToUnicode();
  Utf8ToUnicode(const Utf8ToUnicode&);
  WCHAR* buffer_;
};

class Utf8ToAnsi {
public:
  explicit Utf8ToAnsi(const char* utf8data, unsigned int datalen = -1)
    : buffer_(NULL) {
    int size = MultiByteToWideChar(CP_UTF8, 0, utf8data, datalen, 0, 0);
    if (size > 0) {
      WCHAR* temp = new WCHAR[size + 1];
      if (temp) {
        MultiByteToWideChar(CP_UTF8, 0, utf8data, datalen, temp, size);
        temp[size] = 0;
        size = WideCharToMultiByte(CP_ACP, 0, temp, -1, 0, 0, 0, 0);
        if (size) {
          buffer_ = new char[size];
          WideCharToMultiByte(CP_ACP, 0, temp, -1, buffer_, size, 0, 0);
        }
        delete[] temp;
      }
    }
  }

  operator char*() const { return buffer_; }
  ~Utf8ToAnsi() { if (buffer_) delete[] buffer_; }

private:
  Utf8ToAnsi();
  Utf8ToAnsi(const Utf8ToAnsi&);
  char* buffer_;
};

class AnsiToUtf8 {
public:
  explicit AnsiToUtf8(const char* data, unsigned int datalen = -1) 
    : utf8_(NULL) {
    int size = MultiByteToWideChar(CP_ACP, 0, data, datalen, 0, 0);
    if (size) {
      WCHAR* temp = new WCHAR[size + 1];
      if (temp) {
        MultiByteToWideChar(CP_ACP, 0, data, datalen, temp, size);
        temp[size] = 0;
        size = WideCharToMultiByte(CP_UTF8, 0, temp, -1, 0, 0, 0, 0);
        if (size) {
          utf8_ = new char[size];
          WideCharToMultiByte(CP_UTF8, 0, temp, -1, utf8_, size, 0, 0);
        }
        delete[] temp;
      }
    }
  }
  ~AnsiToUtf8() { if (utf8_) delete[] utf8_; }
  const char* get_utf8() { return utf8_; }

private:
  AnsiToUtf8();
  AnsiToUtf8(const AnsiToUtf8&);
  char* utf8_;
};

class UnicodeToUtf8 {
public:
  explicit UnicodeToUtf8(const WCHAR* data, unsigned int datalen = -1) 
    : utf8_(NULL) {
    int size = WideCharToMultiByte(CP_UTF8, 0, data, datalen, 0, 0, 0, 0);
    if (size) {
      utf8_ = new char[size + 1];
      WideCharToMultiByte(CP_UTF8, 0, data, datalen, utf8_, size, 0, 0);
      utf8_[size] = 0;
    }
  }
  ~UnicodeToUtf8() { if (utf8_) delete[] utf8_; }
  const char* get_utf8() { return utf8_; }

private:
  UnicodeToUtf8();
  UnicodeToUtf8(const UnicodeToUtf8&);
  char* utf8_;
};

#ifdef _UNICODE
#define TCHARToUtf8 UnicodeToUtf8
#define Utf8ToTCHAR Utf8ToUnicode
#else
#define TCHARToUtf8 AnsiToUtf8
#define Utf8ToTCHAR Utf8ToAnsi
#endif

#endif

}

#endif